home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / music / smfplay.lzh / SMFPLAY.S
Text File  |  1993-10-11  |  7KB  |  335 lines

  1.  *** STANDARD MIDI FILE Player ***
  2. *** by Anthony Spirou - October 1992 / 10 October 1993                             ***
  3.  
  4. *** P. O. Box 79409
  5. *** Senderwood
  6. *** 2145
  7. *** South Africa
  8.  
  9. *** Email (definitely until end of 1993): anthony@concave.cs.wits.ac.za
  10.  
  11. *** Feel free to use/modify this code in your own programs.
  12. *** Acknowledgements are nice..
  13.  
  14. * a2: address of the channel filter table (bytes 0-15, -1:on 0:off)
  15. * a4: TI.L (start address always)
  16. * a6: SADR.L (SMF current address)
  17. * d7: TRACKS.w (# of tracks)
  18. * d6: used for track loop counting and the current event byte
  19. * d5: offset of lowest delta time track to track info array
  20. * d4: the current lowest dtime track
  21.  
  22.     section TEXT
  23.     bsr     INIT
  24.     move.l  #$004c0000,-(sp)
  25.     trap    #1
  26.  
  27. INIT
  28.     movem.l d1-d7/a0-a6,-(sp)
  29.     clr.l   -(sp)
  30.     move    #$20,-(sp)
  31.     trap    #1
  32.     addq.l  #6,sp
  33.  
  34.     lea     MIDIFILE(pc),a6   (SMF START ADDRESS)
  35.     lea     CHANFILT(pc),a2   (channel filter address)
  36.  
  37.     cmp.w   #2,8(a6)        (only play files of format 0 or 1)
  38.     bge     ENDERROR
  39.     lea     TI(pc),a4
  40.     move.w  10(a6),d7       (# of tracks)
  41.  
  42. TRACK2TI
  43. * track loop counter in d6 to zero
  44.     clr.w   d6
  45. .TLOOP  addq.w  #1,d6
  46. * a6 ==> start address of next track
  47.     lea     4(a6),a0
  48.     bsr     OLPEEK
  49.     addq.l  #8,d0
  50.     lea     (a6,d0.l),a6
  51. * put event start address into ti%(d6,0)
  52.     move    d6,d5
  53.     lsl     #4,d5
  54.     move.l  a6,(a4,d5.w)
  55.     addq.l  #8,(a4,d5.w)
  56. * put track end address (start of next track) into ti%(d6,1)
  57.     lea     4(a6),a0
  58.     bsr     OLPEEK
  59.     lea     8(a6,d0.l),a0
  60.     move.l  a0,4(a4,d5.w)
  61. * next loop until d6=maxtrack
  62.     cmp.w   d7,d6
  63.     bne     .TLOOP
  64.  
  65. ** PLAY TRACKS
  66. PLAY
  67. * track loop counter d6 to zero
  68.     clr.w   d6
  69. .TLOOP1 addq.w  #1,d6
  70. * put first delta times into ti%(d6,2)
  71.     move.w  d6,d5
  72.     lsl     #4,d5
  73.     move.l  (a4,d5.w),a0
  74.     bsr     VARLEN
  75.     move.l  a0,(a4,d5.w)
  76.     move.l  d0,8(a4,d5.w)
  77.     cmp.w   d7,d6
  78.     bne     .TLOOP1
  79. * next event repeat loop
  80.  
  81. .EVENTLOOP
  82. * find track with lowest current delta time --> d4
  83.     move.l  #$7fffffff,d3
  84.     move.w  #99,d4
  85.     clr.w   d6
  86. .TLOOP2
  87.     addq.w  #1,d6
  88.     move.w  d6,d5
  89.     lsl     #4,d5
  90.     move.l  (a4,d5.w),d0
  91.     move.l  4(a4,d5.w),d1
  92.     cmp.l   d0,d1
  93.     ble     .SKIPTRACK
  94.     move.l  8(a4,d5.w),d1
  95.     cmp.l   d1,d3
  96.     ble     .SKIPTRACK
  97.     move.l  d1,d3
  98.     move.l  d6,d4
  99. .SKIPTRACK
  100.     cmp.w   d7,d6
  101.     bne     .TLOOP2
  102. * finished playing if d6=99 (no lowest found)
  103.     cmp.w   #99,d4
  104.     beq     ENDOK
  105. **** play event from this track
  106. * d5=track info array offset for this lowest-dtime track
  107.     move.w  d4,d5
  108.     lsl     #4,d5
  109. * put current track position address into a6
  110.     move.l  (a4,d5.w),a6
  111. * next event data into d6
  112.     move.b  (a6),d6
  113.     bmi     .NEW_RSBYTE
  114.     bsr     MIDIEVENT
  115.     bra     .NEXTEVENT
  116. .NEW_RSBYTE
  117. * move a6 to past event status byte
  118.     lea     1(a6),a6
  119. * routine for META-event (skip to next event ( event starts with deltatime ))
  120.     cmp.b   #$ff,d6
  121.     bne     .NOTMETA
  122.     cmp.b   #$51,(a6)
  123.     bne     .nosettempo
  124.     lea     2(a6),a0
  125.     bsr     OLPEEK
  126.  
  127.     lsr.l   #8,d0   (24-bit tempo - microseconds/quarternote)
  128.     lea     MIDIFILE+12(pc),a0
  129.     move.w  (a0),d1 (ticks/quarternote)
  130.     divu    d1,d0   (d0 = microseconds/tick)
  131.     and.l   #$ffff,d0
  132.     lsl.l   #1,d0   ( *200/100)
  133. *                       (d0 = clocks.10^-4/tick)
  134. *                       (just need to div. by 10000 to get
  135. *                        number of clocks/tick)
  136. *                        (tick = delta time)
  137.     lea     TEMPO(pc),a0
  138.     move.l  d0,(a0)
  139.  
  140. .nosettempo
  141.     lea     1(a6),a0
  142.     bsr     VARLEN
  143.     move.l  a0,a6
  144.     lea     (a6,d0.l),a6
  145.     clr.l   12(a4,d5.w)         {track running status byte to zero (undefined))
  146.     bra     .NEXTEVENT
  147. .NOTMETA
  148. * move track pointer for SYStem EXclusive event
  149.     cmp.b   #$f0,d6
  150.     blt     .NOTSYSEX
  151.     cmp.b   #$f7,d6
  152.     bgt     .NOTSYSEX
  153.     move.l  a6,a0
  154.     bsr     VARLEN
  155.     move.l  a0,a6
  156.     lea     (a6,d0.l),a6
  157.     clr.l   12(a4,d5.w)
  158.     bra     .NEXTEVENT
  159. .NOTSYSEX
  160.     move.l  d6,12(a4,d5.w)
  161.     bsr     MIDIEVENT
  162. * add next delta time to track delta time
  163. .NEXTEVENT
  164.     move.l  a6,a0
  165.     bsr     VARLEN
  166.     add.l   d0,8(a4,d5.w)
  167.     move.l  a0,(a4,d5.w)
  168.     bra     .EVENTLOOP
  169.  
  170. ******
  171. * MIDI channel messages...
  172. MIDIEVENT
  173. * get track RUNNING STATUS byte to d6 (for current track)
  174.     move.l  12(a4,d5.w),d6
  175. * 2 bytes for PITCH BEND
  176.     cmp.b   #$ef,d6
  177.     bgt     .NOBEND
  178.     cmp.b   #$e0,d6
  179.     blt     .NOBEND
  180. *       lea     2(a6),a6
  181.     bra     SEND2
  182. .NOBEND
  183. * 2 bytes for CONTROL and INSTRUMENT events
  184.     cmp.b   #$bf,d6
  185.     bgt     .NOCONINS
  186.     cmp.b   #$a0,d6
  187.     blt     .NOCONINS
  188. *       lea     2(a6),a6
  189.     bra     SEND2
  190. .NOCONINS
  191. * 1 byte for PROGRAM change
  192.     cmp.b   #$df,d6
  193.     bgt     .NOPRG
  194.     cmp.b   #$c0,d6
  195.     blt     .NOPRG
  196. *       lea     1(a6),a6
  197.     bra     SEND1
  198. .NOPRG
  199. ** 2 bytes for NOTE ON/OFF
  200.     cmp.b   #$9f,d6
  201.     bgt     .NO_NOTE
  202.     bra     SEND2
  203. .NO_NOTE rts
  204.  
  205. ******
  206.  
  207. ENDOK
  208.     clr.l   -(sp)
  209.     move    #$20,-(sp)
  210.     trap    #1
  211.     addq.l  #6,sp
  212.     movem.l (sp)+,d1-d7/a0-a6
  213.     rts
  214. ENDERROR
  215.     clr.l   -(sp)
  216.     move    #$20,-(sp)
  217.     trap    #1
  218.     addq.l  #6,sp
  219.     movem.l (sp)+,d1-d7/a0-a6
  220. *error result in d0
  221.     moveq   #-1,d0
  222.     rts
  223.  
  224. ****************************FUNCTIONS***********************
  225.  
  226. OLPEEK  *input A0=ADDRESS of Odd LPEEK
  227.     *output D0=longword at (a0)
  228.     *       A0=A0+4
  229.     move.b  (a0)+,d0
  230.     lsl.w   #8,d0
  231.     move.b  (a0)+,d0
  232.     swap    d0
  233.     move.b  (a0)+,d0
  234.     lsl.w   #8,d0
  235.     move.b  (a0)+,d0
  236.     rts
  237.  
  238. ODPEEK  *input A0=ADDRESS of Odd LPEEK
  239.     *output D0.w=word at (a0)
  240.     *       A0=A0+2
  241.     move.b  (a0)+,d0
  242.     lsl.w   #8,d0
  243.     move.b  (a0)+,d0
  244.     rts
  245.  
  246. VARLEN  *input A0=ADDRESS of variable length number
  247.     *ouput D0.l=NUMBER
  248.     *      D1=smashed
  249.     *      A0=Address after varlen number
  250.     clr.l   d0
  251. .vloop  lsl.l   #7,d0
  252.     move.b  (a0)+,d1
  253.     and.b   #$7f,d1
  254.     add.b   d1,d0
  255.     btst.b  #7,-1(a0)
  256.     bne     .vloop
  257.     rts
  258.  
  259.  
  260. SEND1   
  261.     bsr     PAUSE
  262.  
  263.     move    d6,d0
  264.     bsr     SEND
  265.     move.b  (a6)+,d0
  266.     bsr     SEND
  267.     bra     CHANGE_DTIMES
  268. SEND2   bsr     PAUSE
  269.     move    d6,d0
  270.     bsr     SEND
  271.     move.b  (a6)+,d0
  272.     bsr     SEND
  273.     move.b  (a6)+,d0
  274.     bsr     SEND
  275. CHANGE_DTIMES
  276. * get accumulated delta time to d0
  277.     move.l  8(a4,d5.w),d0
  278. * subtract this dtime from all track dtimes (ended tracks may become -ve)
  279.     clr.w   d1
  280. .TLOOP
  281.     addq.w  #1,d1
  282.     move    d1,d2
  283.     lsl     #4,d2
  284.     sub.l   d0,8(a4,d2.w)
  285.     cmp.w   d7,d1
  286.     bne     .TLOOP
  287.     rts
  288.  
  289. PAUSE
  290. * get accumulated delta time to d0
  291.     move.l  8(a4,d5.w),d0
  292.     move.l  TEMPO(pc),d1
  293.     mulu    d0,d1
  294.     divu    #10000,d1
  295.     and.l   #$ffff,d1
  296.     move.l  $4ba.w,d0
  297.     add.l   d1,d0
  298. .wait   move.l  $4ba.w,d2
  299.     cmp.l   d0,d2
  300.     blt     .wait
  301.     rts
  302.  
  303.  
  304. SEND    movem.l d0-d2/a0-a2,-(sp)
  305.  
  306. *skip if channel off in filter table
  307.     move.b  d6,d0
  308.     and.w   #$000f,d0
  309.     tst.b   (a2,d0.w)
  310.     beq     .FILTER
  311.     movem.l (sp),d0-d2/a0-a2
  312.     move    d0,-(sp)
  313.     move.l  #$00030003,-(sp)
  314.     trap    #13
  315.     addq.l  #6,sp
  316. .FILTER
  317.     movem.l (sp)+,d0-d2/a0-a2
  318.     rts
  319.  
  320. ******************************DATA*******************************
  321.     section DATA
  322. TEMPO   ds.l    1
  323. TI      ds.l    64*4
  324.  
  325. CHANFILT dc.b   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
  326.  
  327. MIDIFILE incbin d:*.mid
  328.     even
  329.  
  330.  
  331. *******************************BSS*******************************
  332.     section BSS
  333.  
  334. ALLEND
  335.